using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using Microsoft.Xna.Framework;
using Microsoft.Xna.Framework.Graphics;
using Microsoft.Xna.Framework.Audio;

namespace ebookreader
{
    public delegate void Press(MenuEntry entry);
    public delegate void Hold(MenuEntry entry);

    public abstract class MenuEntry
    {
        #region Fields

        public static SoundEffect press_sound;

        public event Press OnPress;
        public event Hold OnHolded;
        public event Press OnDoubleTapped;

        public Vector2 position;
        protected Vector2 _scrollOffset;
        public Vector2 scrollOffset { get { return _scrollOffset; } set { _scrollOffset = scrollable ? value : _scrollOffset; } }

        public Rectangle rectangle;
        protected SpriteBatch sprite_batch;
        protected float alpha = 1.0f;

        public bool scrollable { get; protected set; }
        public bool active = true;
        public bool touched;
        optionsData current_options;
        #endregion

        public MenuEntry(optionsData current_options)
        {
            scrollable = false;
            this.current_options = current_options;
            if (current_options == null)
                ;
        }


        #region Draw

        public abstract void DrawInsideSpriteBatch();        

        public void DrawOutsideSpriteBatch()
        {
            sprite_batch.Begin();

            DrawInsideSpriteBatch();

            sprite_batch.End();
        }

        public void DrawInsideSpriteBatch(float alpha)
        {
            this.alpha = alpha;

            DrawInsideSpriteBatch();

            this.alpha = 1.0f;
        }

        #endregion

        #region Events Delegate

        public void OnTap(Vector2 tap_position)
        {
            if (OnPress != null && rectangle.Contains((int)tap_position.X, (int)tap_position.Y))
            {
                OnPress(this);
                if (press_sound != null && current_options.audio)
                    press_sound.Play();
            }
        }

        public void OnHold(Vector2 hold_position)
        {
            if (OnHolded != null  && rectangle.Contains((int)hold_position.X, (int)hold_position.Y))
                OnHolded(this);
        }

        public void OnDoubleTap(Vector2 tap_position)
        {
            if (OnDoubleTapped != null && rectangle.Contains((int)tap_position.X, (int)tap_position.Y))
                OnDoubleTapped(this);
        }
        #endregion
    }

    public class LabelButton : MenuEntry
    {
        #region Fields

        Color color, shadow_color;
        Vector2 shadow_position;
        SpriteFont font;
        public string label;

        #endregion

        #region Constructors

        public LabelButton(SpriteBatch sprite_batch, SpriteFont font, Vector2 position, Color color, Color shadow_color, string label, optionsData optionData)
            :base(optionData)
        {
            Vector2 label_size = font.MeasureString(label);

            this.color = color;
            this.shadow_color = shadow_color;
            this.font = font;
            this.sprite_batch = sprite_batch;
            this.label = label;
            this.position = position;
            this.shadow_position = position + Vector2.One * 2.0f;
            this.rectangle = new Rectangle((int)position.X, (int)position.Y, (int)label_size.X, (int)label_size.Y);            
        }

        public LabelButton(SpriteBatch sprite_batch, SpriteFont font, Vector2 position, Color color, string label, optionsData optionData)
            : this(sprite_batch, font, position, color, Color.Transparent, label, optionData)
        { }

        public LabelButton(SpriteBatch sprite_batch, SpriteFont font, Vector2 position, Color color, Color shadow_color, string label, int padding, optionsData optionData)
            : this(sprite_batch, font, position, color, shadow_color, label, optionData)
        {
            rectangle.X -= padding;
            rectangle.Y -= padding;
            rectangle.Width += padding * 2;
            rectangle.Height += padding * 2;
        }

        public LabelButton(SpriteBatch sprite_batch, SpriteFont font, Vector2 position, Color color, string label, int padding, optionsData optionData)
            : this(sprite_batch, font, position, color, Color.Transparent, label, padding, optionData)
        { }

        #endregion

        #region Draw Methods

        public override void DrawInsideSpriteBatch()
        {
            sprite_batch.DrawString(font, label, shadow_position, shadow_color);
            sprite_batch.DrawString(font, label, position, color);
        }

        #endregion

        #region Proprieties

        #endregion
    }

    public class TextureButton : MenuEntry
    {
        #region Fields

        public Color color;
        public Texture2D texture;
        public Rectangle draw_rectangle;
        #endregion

        #region Constructors

        public TextureButton(SpriteBatch sprite_batch, Texture2D texture, Vector2 position, Color color, optionsData optionData)
            :base(optionData)
        {
            this.color = color;
            this.sprite_batch = sprite_batch;
            this.position = position;
            this.texture = texture;
            this.draw_rectangle = new Rectangle((int)position.X, (int)position.Y, texture.Width, texture.Height);
            this.rectangle = draw_rectangle;
        }

        public TextureButton(SpriteBatch sprite_batch, Texture2D texture, Vector2 position, optionsData optionData)
            : this(sprite_batch, texture, position, Color.White, optionData)
        { }

        public TextureButton(SpriteBatch sprite_batch, Texture2D texture, Vector2 position, Color color, int padding, optionsData optionData)
            : this(sprite_batch, texture, position, color, optionData)
        {
            draw_rectangle.X -= padding;
            draw_rectangle.Y -= padding;
            draw_rectangle.Width += padding * 2;
            draw_rectangle.Height += padding * 2;
        }

        public TextureButton(SpriteBatch sprite_batch, Texture2D texture, Vector2 position, Color color, Rectangle touch_rectangle, optionsData optionData)
            : this(sprite_batch, texture, position, color, optionData)
        {
            rectangle = touch_rectangle;
        }

        public TextureButton(SpriteBatch sprite_batch, Texture2D texture, Vector2 position, int padding, optionsData optionData)
            : this(sprite_batch, texture, position, Color.White, padding, optionData)
        { }

        #endregion

        #region Draw Methods

        public override void DrawInsideSpriteBatch()
        {
            sprite_batch.Draw(texture, draw_rectangle, color * alpha);
        }

        #endregion

        #region Proprieties

        #endregion
    }

    public class BoxedLabelButton : MenuEntry
    {
        #region Fields

        Color color, shadow_color, box_color;
        Vector2 shadow_position;
        public Vector2 _box_position;
        Texture2D _box;


        public Vector2 box_position
        {
            get { return _box_position; }
            set
            {
                var offset = value - _box_position;
                if (offset == Vector2.Zero) return;
                shadow_position += offset;                
                Vector2 label_size = font.MeasureString(label);                
                _box_position = value;
                this.position = new Vector2(box_position.X + (center ? _box.Width / 2 - label_size.X / 2 : 20),
                box_position.Y + (box.Height / 2.0f - label_size.Y / 2.0f)) + label_offset;
                rectangle = new Rectangle((int)box_position.X, (int)box_position.Y, box.Width, box.Height);
            }
        }
        public Texture2D box
        {
            get { return _box; }
            set
            {
                var offsetX = (_box.Width - value.Width) / 2;
                var offsetY = (_box.Height - value.Width) / 2;
                _box_position += new Vector2(offsetX, offsetY);
                shadow_position += new Vector2(offsetX, offsetY);
            }
        }

        SpriteFont font;
        StringBuilder label;

        #endregion

        #region Constructors
        bool center;
        Vector2 label_offset;
        public BoxedLabelButton(SpriteBatch sprite_batch, Texture2D box, Vector2 box_position, Color box_color, SpriteFont font, 
            Vector2 label_offset, StringBuilder label, Color color, Color shadow_color, bool center, optionsData optionState)
            : base(optionState)
        {
            Vector2 label_size = font.MeasureString(label);

            this._box = box;
            this.box_color = box_color;
            this._box_position = box_position;
            this.color = color;
            this.shadow_color = shadow_color;
            this.font = font;
            this.sprite_batch = sprite_batch;
            this.label = label;
            this.center = center;
            this.label_offset = label_offset;
            //this.position = new Vector2(box_position.X + (box.Width / 2.0f - label_size.X / 2.0f), 
            //    box_position.Y + (box.Height / 2.0f - label_size.Y / 2.0f)) + label_offset;
            this.position = new Vector2(box_position.X + (center?_box.Width/2-label_size.X/2:20) , 
                box_position.Y + (box.Height / 2.0f - label_size.Y / 2.0f)) + label_offset;
            this.shadow_position = position + Vector2.One * 2.0f;
            this.rectangle = new Rectangle((int)box_position.X, (int)box_position.Y, box.Width, box.Height);
        }

        public BoxedLabelButton(SpriteBatch sprite_batch, Texture2D box, Vector2 box_position, Color box_color, SpriteFont font,
            Vector2 label_offset, string label, Color color, Color shadow_color, bool center, optionsData optionState)
            : this(sprite_batch, box, box_position, Color.White, font, label_offset, new StringBuilder(label), color, shadow_color, center, optionState)
        {
        }

        public BoxedLabelButton(SpriteBatch sprite_batch, Texture2D box, Vector2 box_position, SpriteFont font,
            Vector2 label_offset, string label, Color color, Color shadow_color, optionsData optionState)
            : this(sprite_batch, box, box_position, Color.White, font, label_offset, label, color, shadow_color, false, optionState)
        { }


        public BoxedLabelButton(SpriteBatch sprite_batch, Texture2D box, Vector2 box_position, SpriteFont font,
    Vector2 label_offset, StringBuilder label, Color color, Color shadow_color, optionsData optionState)
            : this(sprite_batch, box, box_position, Color.White, font, label_offset, label, color, shadow_color, false, optionState)
        { }

        public BoxedLabelButton(SpriteBatch sprite_batch, Texture2D box, Vector2 box_position, SpriteFont font,
            string label, Color color, Color shadow_color, optionsData optionState)
            : this(sprite_batch, box, box_position, Color.White, font, Vector2.Zero, label, color, shadow_color, false, optionState)
        { }

        public BoxedLabelButton(SpriteBatch sprite_batch, Texture2D box, Vector2 box_position, SpriteFont font,
           string label, Color color, Color shadow_color, bool center, optionsData optionState)
            : this(sprite_batch, box, box_position, Color.White, font, Vector2.Zero, label, color, shadow_color, center, optionState)
        { }

        public BoxedLabelButton(SpriteBatch sprite_batch, Texture2D box, Vector2 box_position, SpriteFont font,
            string label, Color color, bool center, optionsData optionState)
            : this(sprite_batch, box, box_position, Color.White, font, Vector2.Zero, label, color, Color.Transparent, center, optionState)
        { }

        public BoxedLabelButton(SpriteBatch sprite_batch, Texture2D box, Vector2 box_position, SpriteFont font,
            Vector2 label_offset, string label, Color color, bool center, optionsData optionState)
            : this(sprite_batch, box, box_position, Color.White, font, label_offset, label, color, Color.Transparent, center, optionState)
        { }

        #endregion

        #region Draw Methods

        public override void DrawInsideSpriteBatch()
        {
            sprite_batch.Draw(_box, rectangle, box_color * alpha * ((touched&&active) ? 0.5f : 1));
            sprite_batch.DrawString(font, label, shadow_position, shadow_color * alpha);
            sprite_batch.DrawString(font, label, position, color * alpha);
        }

        #endregion

        #region Proprieties

        public string Label
        {
            get { return label.ToString(); }
        }

        #endregion
    }


    public class BoxedLabelScrollableButton : MenuEntry
    {
        #region Fields

        public Color color, shadow_color, box_color;
        Vector2 shadow_position;
        private Vector2 _box_position;
        bool centered = false;

        public Vector2 box_position {
            get{ return _box_position; } 
            set{ 
                var offset = value - _box_position;
                position+=offset;
                shadow_position+=offset;
                rectangle.Offset((int)offset.X, (int)offset.Y);
                //rectangle.Location = new Point(rectangle.Location.X + (int)offset.X,rectangle.Location.Y + (int)offset.Y);
                _box_position=value;
                for (int i = 0; i < labels.Count; i++)
                {
                    positions[i] += offset;
                    shadow_positions[i] += offset;
                }
            }
        }
        Texture2D _box;

        public Texture2D box
        {
            get { return _box; }
            set
            {
                var offsetX = (_box.Width - value.Width) / 2;
                var offsetY = (_box.Height - value.Height) / 2;


                splitLabel(label);
                box_position += new Vector2(offsetX, offsetY);
                this.rectangle = new Rectangle((int)box_position.X, (int)box_position.Y, box.Width, box.Height);
                //shadow_position += new Vector2(offsetX, offsetY);
                _box = value;
            }
        }


        SpriteFont font;
        string label;

        #endregion


        List<Vector2> positions=new List<Vector2>(), shadow_positions=new List<Vector2>();
        List<string> labels= new List<string>();

        bool splitted=false;
        public void splitLabel()
        {
            if (!splitted)
                splitLabel(label);
            splitted = true;
        }

        void splitLabel(string label)
        {
            if (splitted) return;
            labels.Clear();
            positions.Clear();
            shadow_positions.Clear();

            var ls = label.Split(new char[] { ' ' });
            var c_l = "";
            for (int i = 0; i < ls.Length; i++)
            {
                if (labels.Count > 2)
                {
                    labels[2]+="...";
                    break;
                };
                var l = " "+ls[i];
                while (font.MeasureString(l).X >= _box.Width)
                {
                    l = l.Substring(0, l.Length/2)+"...";
                }
                if (font.MeasureString(c_l + l).X < _box.Width)
                    c_l += l;
                else
                {                    
                    labels.Add(c_l);
                    c_l = "";
                    i--;
                }
            }
            if (!c_l.Equals("")) labels.Add(c_l);

            for (int i = 0; i < labels.Count; i++)
            {
                var l = labels[i];
                var pos = new Vector2(box_position.X + (_box.Width / 2.0f - font.MeasureString(l).X / 2.0f),
                    box_position.Y + _box.Height / 2 + ((i  - labels.Count/2) * (font.MeasureString("I").Y)) - font.MeasureString("I").Y/2);
                positions.Add(pos);
                shadow_positions.Add(pos + Vector2.One * 2.0f);

            }
            
        }

        #region Constructors

        public BoxedLabelScrollableButton(SpriteBatch sprite_batch, Texture2D box, Vector2 box_position, Color box_color, SpriteFont font,
            Vector2 label_offset, string label, Color color, Color shadow_color, optionsData optionState)
            :base(optionState)
        {
            //foreach (var c in label)
            //{
            //    if (!font.Characters.Contains(c))
            //    {
            //        label = label.Replace(c,'?');
            //    };
            //}
            Vector2 label_size = font.MeasureString(label);

            this._box = box;
            this.box_color = box_color;
            this._box_position = box_position;
            this.color = color;
            this.shadow_color = shadow_color;
            this.font = font;
            this.sprite_batch = sprite_batch;
            this.label = label;            
            this.position = new Vector2(box_position.X + (box.Width / 2.0f - label_size.X / 2.0f),
            box_position.Y + (box.Height / 2.0f - label_size.Y / 2.0f)) + label_offset;
            this.shadow_position = position + Vector2.One * 2.0f;
            this.rectangle = new Rectangle((int)box_position.X, (int)box_position.Y, box.Width, box.Height);
                       
            //splitLabel(label);

            this.scrollable = true;
        }

        public BoxedLabelScrollableButton(SpriteBatch sprite_batch, Texture2D box, Vector2 box_position, SpriteFont font,
            Vector2 label_offset, string label, Color color, Color shadow_color, optionsData optionState)
            : this(sprite_batch, box, box_position, Color.White, font, label_offset, label, color, shadow_color, optionState)
        { }

        public BoxedLabelScrollableButton(SpriteBatch sprite_batch, Texture2D box, Vector2 box_position, SpriteFont font,
            string label, Color color, Color shadow_color, optionsData optionState)
            : this(sprite_batch, box, box_position, Color.White, font, Vector2.Zero, label, color, shadow_color, optionState)
        { }

        public BoxedLabelScrollableButton(SpriteBatch sprite_batch, Texture2D box, Vector2 box_position, SpriteFont font,
            string label, Color color, optionsData optionState)
            : this(sprite_batch, box, box_position, Color.White, font, Vector2.Zero, label, color, Color.Transparent, optionState)
        { }

        public BoxedLabelScrollableButton(SpriteBatch sprite_batch, Texture2D box, Vector2 box_position, SpriteFont font,
            Vector2 label_offset, string label, Color color, optionsData optionState)
            : this(sprite_batch, box, box_position, Color.White, font, label_offset, label, color, Color.Transparent, optionState)
        { }

        #endregion

        #region Draw Methods

        public override void DrawInsideSpriteBatch()
        {


            var o_rectangle = rectangle;
            o_rectangle.Offset((int)_scrollOffset.X, (int)_scrollOffset.Y);
            //o_rectangle.Location = new Point((int)_scrollOffset.X + o_rectangle.Location.X, (int)_scrollOffset.Y + o_rectangle.Location.Y);
            
            
            sprite_batch.Draw(_box, o_rectangle, box_color * alpha);
            //sprite_batch.DrawString(font, label, o_shadow_position, shadow_color * alpha);
            //sprite_batch.DrawString(font, label, o_position, color * alpha);
            for (int i = 0; i < labels.Count; i++)
            {
                var o_shadow_position = shadow_positions[i] + _scrollOffset;
                var o_position = positions[i] + _scrollOffset;
                sprite_batch.DrawString(font, labels[i], o_shadow_position, shadow_color * alpha);
                sprite_batch.DrawString(font, labels[i], o_position, color * alpha);
            }
        }

        #endregion

        #region Proprieties

        public string Label
        {
            get { return label; }
        }

        #endregion
    }
}
